// NativeMathWrapper.h

#pragma once

#include "NativeMath.h"

#define BUFFER_SIZE 1024

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;

namespace Weazel
{
	namespace NativeMath
	{
		public ref class Integer
		{
		public:
			Integer(const int width, const bool isSigned) 
			{
				_integer = newint(width, isSigned);
			}

			Integer(const int width, const int value, const bool isSigned) 
			{
				_integer = newint(width, isSigned);
				assignint(value, _integer);
			}

			Integer(const int width, const unsigned int value, const bool isSigned) 
			{
				_integer = newint(width, isSigned);
				assignuint(value, _integer);
			}

			Integer(const int width, const long long int value, const bool isSigned) 
			{
				_integer = newint(width, isSigned);
				assignlong(value, _integer);
			}

			Integer(const int width, String^ value, const bool isSigned) 
			{
				_integer = newint(width, isSigned);
				assignstr((const char*)Marshal::StringToHGlobalAnsi(value).ToPointer(), _integer);
			}

			Integer(String^ value)
			{
				const char* cp = (const char*)Marshal::StringToHGlobalAnsi(value).ToPointer();
				_integer = newintfromstr(cp);
			}

			void Assign(const int value) 
			{
				assignint(value, _integer);
			}

			void Assign(const unsigned int value) 
			{
				assignuint(value, _integer);
			}

			void Assign(const long long int value) 
			{
				assignlong(value, _integer);
			}

			void Assign(String^ value) 
			{
				assignstr((const char*)Marshal::StringToHGlobalAnsi(value).ToPointer(), _integer);
			}

			void AssignBinaryString(String^ value) 
			{
				assignbinstr((const char*)Marshal::StringToHGlobalAnsi(value).ToPointer(), _integer);
			}

			void AssignDecimalString(String^ value) 
			{
				assigndecstr((const char*)Marshal::StringToHGlobalAnsi(value).ToPointer(), _integer);
			}

			void AssignHexadecimalString(String^ value) 
			{
				assignhexstr((const char*)Marshal::StringToHGlobalAnsi(value).ToPointer(), _integer);
			}

			// copy value of integer to this instance
			void ValueCopy(Integer^ value)
			{
				valuecopy(value->_integer, _integer);
			}

			String^ ToBinaryString()
			{
				char buffer[BUFFER_SIZE] = "";
				tobinstr(_integer, buffer, BUFFER_SIZE);
				return Marshal::PtrToStringAnsi((IntPtr)buffer);
			}

			String^ ToDecimalString()
			{
				char buffer[BUFFER_SIZE] = "";
				todecstr(_integer, buffer, BUFFER_SIZE);
				return Marshal::PtrToStringAnsi((IntPtr)buffer);
			}

			String^ ToHexadecimalString()
			{
				char buffer[BUFFER_SIZE] = "";
				tohexstr(_integer, buffer, BUFFER_SIZE);
				return Marshal::PtrToStringAnsi((IntPtr)buffer);
			}

			String^ ToString() new
			{
				return ToDecimalString();
			}

			String^ ToString(const int base) 
			{
				switch(base) 
				{
				case 2:
					return ToBinaryString();
				case 10:
					return ToDecimalString();
				case 16:
					return ToHexadecimalString();
				default:
					throw gcnew NotSupportedException();
				}
			}

			int ToInt()
			{
				return toint(_integer);
			}

			unsigned int ToUInt()
			{
				return touint(_integer);
			}

			bool IsZero()
			{
				return iszero(_integer);
			}

			bool GetBit(const int position)
			{
				return getbit(_integer, position);
			}

			void SetBit(const int position)
			{
				setbit(_integer, position);
			}

			void ClearBit(const int position)
			{
				clrbit(_integer, position);
			}

			bool Equals(Object^ obj) new
			{
				if(GetType() != obj->GetType())
					return false;

				return this == (Integer^)obj;
			}

			// properties

			property bool Signed
			{
				bool get()
				{
					return _integer->issigned;
				}
			}

			property int Width
			{
				int get()
				{
					return _integer->width;
				}
			}

			property array<unsigned int>^ Content
			{
				array<unsigned int>^ get()
				{
					const int length = _integer->content->length;
					List<unsigned int>^ content = gcnew List<unsigned int>(length);
					for(int i = 0; i < length; i++)
						content->Add(_integer->content->values[i]);
					return content->ToArray();
				}
			}

			

			// static methods ...

			static void Add(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				add(a->_integer, b->_integer, result->_integer);
			}

			static void Subtract(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				sub(a->_integer, b->_integer, result->_integer);
			}

			static void Multiply(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				mul(a->_integer, b->_integer, result->_integer);
			}

			static void Divide(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				throw gcnew NotImplementedException("Long Division is not implemented");
			}

			static void Mod(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				throw gcnew NotImplementedException("Modulo is not implemented");
			}

			static void Negate(const Integer^ result, const Integer^ a)
			{
				negate(a->_integer, result->_integer);
			}

			static void And(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				_and(a->_integer, b->_integer, result->_integer);
			}

			static void Or(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				ior(a->_integer, b->_integer, result->_integer);
			}

			static void Xor(const Integer^ result, const Integer^ a, const Integer^ b)
			{
				_xor(a->_integer, b->_integer, result->_integer);
			}

			static void Not(const Integer^ result, const Integer^ value)
			{
				_not(value->_integer, result->_integer);
			}

			static void LeftShift(const Integer^ result, const Integer^ a, const int value)
			{
				shl(a->_integer, value, result->_integer);
			}
			
			static void RightShift(const Integer^ result, const Integer^ a, const int value)
			{
				shr(a->_integer, value, result->_integer);
			}

			static void GetRange(const Integer^ result, const Integer^ a, const int index1, const int index2)
			{
				getrange(a->_integer, index1, index2, result->_integer);
			}

			// operator overloads
			static Integer^ operator+ (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				Add(result, a, b);
				return result;
			}

			static Integer^ operator- (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				Subtract(result, a, b);
				return result;
			}

			static Integer^ operator* (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				Multiply(result, a, b);
				return result;
			}

			static Integer^ operator& (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				And(result, a, b);
				return result;
			}

			static Integer^ operator| (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				Or(result, a, b);
				return result;
			}

			static Integer^ operator^ (Integer^ a, Integer^b)
			{
				Integer^ result = gcnew Integer(MAX(a->Width, b->Width), a->Signed || b->Signed);
				Xor(result, a, b);
				return result;
			}

			static Integer^ operator~ (Integer^ value)
			{
				Integer^ result = gcnew Integer(value->_integer->width, value->_integer->issigned);
				Not(result, value);
				return result;
			}

			static Integer^ operator<< (Integer^ a, unsigned int b)
			{
				Integer^ result = gcnew Integer(a->_integer->width, a->_integer->issigned);
				LeftShift(result, a, b);
				return result;
			}

			static Integer^ operator>> (Integer^ a, unsigned int b)
			{
				Integer^ result = gcnew Integer(a->_integer->width, a->_integer->issigned);
				RightShift(result, a, b);
				return result;
			}

			static bool operator== (const Integer^ a, const Integer^ b)
			{
				return areequal(a->_integer, b->_integer);
			}
			
			static bool operator!= (const Integer^ a, const Integer^ b)
			{
				return !areequal(a->_integer, b->_integer);
			}

			static bool operator> (const Integer^ a, const Integer^ b)
			{
				return isgreater(a->_integer, b->_integer);
			}

			static bool operator>= (const Integer^ a, const Integer^ b)
			{
					return (a == b) || (a > b);
			}

			static bool operator< (const Integer^ a, const Integer^ b)
			{
				return b > a;
			}

			static bool operator<= (const Integer^ a, const Integer^ b)
			{
				return (a == b) || (b > a);
			}			

		private:

			ninteger* _integer;
		};
	}
}
